package com.lagou.mvcframework.servlet;

import com.lagou.mvcframework.annoation.*;
import com.lagou.mvcframework.pojo.Handler;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LgDispatcherServlet extends HttpServlet {
    private Properties properties = new Properties();

    @Override
    public void init(ServletConfig config) throws ServletException {
        String contextConfigLocaton = config.getInitParameter("contextConfigLocaton");
        //加载配置文件
        loadConfig(contextConfigLocaton);
        //扫描类
        doScanPagkage(properties.getProperty("scanPackage"));
        //初始化Bean对象
        doInstance();
        //实现依赖注入
        doAutoWried();
        //初始化url映射
        initHanderMapping();

    }

    private Handler loadSecurity(Class<?> aClass,Handler handler) {
        List<String[]> values = new ArrayList<>();
        int arrLength=0;
        if(aClass.isAnnotationPresent(Security.class)){
            arrLength++;
            Security classAnnotation = aClass.getAnnotation(Security.class);
            String[] typeSecurityValues = classAnnotation.value();
            values.add(typeSecurityValues);
            Method[] methods = aClass.getMethods();
            for (int i = 0; i < methods.length; i++) {
                Method method = methods[i];
                if(method.isAnnotationPresent(Security.class)){
                    arrLength++;
                    Security methodAnnotation = method.getAnnotation(Security.class);
                    String[] methodSecurityValues = methodAnnotation.value();
                    values.add(methodSecurityValues);

                }
            }
        }
        String[] target = new String[arrLength];
        int count=0;
        for (int i = 0; i < values.size(); i++) {
            String[] strings = values.get(i);
            for (int j = 0; j < strings.length; j++) {
                count++;
                target[count] = strings[j];
            }
        }
        handler.setSecrity(target);
        return handler;
    }

//    Map<String, Method> handlerMapping = new HashMap<>();

    List<Handler> handlerMapping = new ArrayList<>();
    private void initHanderMapping() {
        if (!ioc.isEmpty()) {
            for (Map.Entry<String, Object> entry : ioc.entrySet()) {
                Class<?> aClass = entry.getValue().getClass();
                if (aClass.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping requestMapping = aClass.getAnnotation(RequestMapping.class);
                    String controllerPath = requestMapping.value();
                    Method[] methods = aClass.getMethods();
                    for (int i = 0; i < methods.length; i++) {
                        if (methods[i].isAnnotationPresent(RequestMapping.class)) {
                            RequestMapping methodR = methods[i].getAnnotation(RequestMapping.class);
                            String value = methodR.value();
                            Handler handler= new Handler(entry.getValue(),methods[i], Pattern.compile(controllerPath+value));
                            handlerMapping.add(handler);
                             loadSecurity(aClass,handler);
                            Parameter[] parameters = methods[i].getParameters();
                            for (int j = 0; j < parameters.length; j++) {
                                Parameter parameter = parameters[j];
                                if(parameter.getType() == HttpServletRequest.class || parameter.getType() == HttpServletResponse.class){
                                    handler.getParamIndexMapping().put(parameter.getType().getSimpleName(),j);
                                }else {
                                    handler.getParamIndexMapping().put(parameter.getName(),j);
                                }
                            }

                        }
                    }
                }
            }
        }

    }

    private void doAutoWried() {
        if (!ioc.isEmpty()) {
            for (Map.Entry<String, Object> entry : ioc.entrySet()) {
                Field[] declaredFields = entry.getClass().getDeclaredFields();
                for (int i = 0; i < declaredFields.length; i++) {
                    Field field = declaredFields[i];
                    if (field.isAnnotationPresent(AutoWried.class)) {
                        AutoWried annotation = field.getAnnotation(AutoWried.class);
                        String beanName = annotation.value();
                        Object obj = null;
                        if (!"".equals(beanName)) {
                            obj = ioc.get(beanName);
                        } else {
                            beanName = field.getType().getName();
                            obj = ioc.get(beanName);
                        }

                        field.setAccessible(true);
                        try {
                            field.set(entry.getValue(), obj);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }

                    }
                }

            }
        }
    }

    private Map<String, Object> ioc = new HashMap<>();

    private void doInstance() {
        if (!classNames.isEmpty()) {
            try {
                for (int i = 0; i < classNames.size(); i++) {
                    String className = classNames.get(i);
                    Class<?> aClass = Class.forName(className);
                    if (aClass.isAnnotationPresent(Controller.class)) {
                        String lowerFirstCase = lowerFirstCase(aClass.getSimpleName());
                        ioc.put(lowerFirstCase, aClass.newInstance());
                    } else if (aClass.isAnnotationPresent(Service.class)) {
                        Service annotation = aClass.getAnnotation(Service.class);
                        String value = annotation.value();
                        if (value.equals("")) {
                            String lowerFirstCase = lowerFirstCase(aClass.getSimpleName());
                            ioc.put(lowerFirstCase, aClass.newInstance());
                        } else {
                            ioc.put(value, aClass.newInstance());
                        }

                        Class<?>[] interfaces = aClass.getInterfaces();
                        for (int j = 0; j < interfaces.length; j++) {
                            ioc.put(interfaces[i].getSimpleName(), aClass.newInstance());
                        }
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }


    }

    private String lowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    private List<String> classNames = new ArrayList<>();

    private void doScanPagkage(String scanPages) {
        //获取classpath磁盘路径
        String path = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPages;
        String filePath = path.replace("\\.", "/");
        File file = new File(filePath);
        File[] files = file.listFiles();
        for (int i = 0; i < files.length; i++) {
            if (files[i].isDirectory()) {
                doScanPagkage(scanPages + "." + files[i].getName());

            } else if (files[i].getName().endsWith(".class")) {
                String className = scanPages + "." + files[i].getName().replaceAll(".class", "");
                classNames.add(className);
            }
        }
    }

    private void loadConfig(String contextConfigLocaton) {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocaton);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        String requestURI = req.getRequestURI();
//        Method method = handlerMapping.get(requestURI);
        Handler handler=  getHandler(req);
        if(Objects.isNull(handler)){
            resp.getWriter().print("404");
            return;
        }
         //根据用户名获取用户信息
        String userId = req.getParameter("userId");
        //TODO 根据uid获取用户信息和权限
        List<String> userAuto=null;
        //获取注解配置权限
        String[] secrity = handler.getSecrity();
        for (int i = 0; i < secrity.length; i++) {
            if(!userAuto.contains(secrity[i])){
                resp.getWriter().print("没有权限");
                return;
            }
        }


        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
        Object[] objValue = new Object[parameterTypes.length];
        Map<String, String[]> parameterMap = req.getParameterMap();
         for (Map.Entry<String,String[]> entry: parameterMap.entrySet()) {
             String[] value = entry.getValue();
             String join = StringUtils.join(value, ",");
             if(handler.getParamIndexMapping().containsKey(entry.getKey())){
                 Integer paramIndex = handler.getParamIndexMapping().get(entry.getKey());
                 objValue[paramIndex] =join;
             }
         }

        Integer reqIndex = handler.getParamIndexMapping().get(req.getClass().getSimpleName());
        Integer respIndex = handler.getParamIndexMapping().get(resp.getClass().getSimpleName());


        try {
            handler.getMethod().invoke(handler.getController(),objValue);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }


    }

    private Handler getHandler(HttpServletRequest req) {
        String requestURI = req.getRequestURI();
        for (Handler handler : handlerMapping) {
            Matcher matcher = handler.getPattern().matcher(requestURI);
            if(matcher.matches()){
                return  handler;
            }
        }
        return  null;

    }
}
